/*
 * Composition Knowledge Miner. A data mashup composition knowledge miner
 * Copyright (C) 2011 Carlos Rodriguez
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package eu.ict_omelette.mashup.compositionminer;

import eu.ict_omelette.mashup.canonicalmodel.CanonicalMashup;
import eu.ict_omelette.mashup.canonicalmodel.Component;
import eu.ict_omelette.mashup.canonicalmodel.Dataflow;
import eu.ict_omelette.mashup.canonicalmodel.InputParameter;
import eu.ict_omelette.mashup.canonicalmodel.InputPort;
import eu.ict_omelette.mashup.canonicalmodel.OutputPort;
import eu.ict_omelette.mashup.compositionminer.util.Carpenter;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;

/**
 *
 * @author Carlos Rodriguez
 */
public class ComponentCoOccurrenceMiner {
    private LinkedList<CanonicalMashup> compRepo;
    private final String ITEM_SEPARATOR = "&nbsp;";

    public ComponentCoOccurrenceMiner(LinkedList<CanonicalMashup> compRepo){
        this.compRepo = compRepo;
    }

    public List<CanonicalMashup> mine(Double minSupDf, Double minSupDm, Double minSupVa){
        LinkedList<CanonicalMashup> result = new LinkedList();
        LinkedHashMap hashReccDf = Util.getRecurrentConnectors(compRepo, minSupDf);

        //System.out.println("Leading Pattern Count: " + hashReccDf.size());

        LinkedList<Dataflow> llDfs = Util.getConnectorInstances(compRepo, hashReccDf);

        LinkedHashMap<String, LinkedList> hashDFTypes = new LinkedHashMap();

        //In the following, we create a HashMap where the key is a "signature" of the
        //dataflow and the value is a LinkedList that contains all instances of dataflows
        //that match the given signature
        for(int i = 0; i < llDfs.size(); i++){
            Dataflow df = llDfs.get(i);

            String strFromComp = df.getFromComponent().getName();
            String strFromPort = df.getFromPort().getName();

            String strToComp = df.getToComponent().getName();
            String strToPort = df.getToPort().getName();

            String strHashDf = strFromComp + "." + strFromPort + "=>" +
                               strToComp   + "." + strToPort;

            //System.out.println(strHashDf);

            LinkedList llDFInst = hashDFTypes.get(strHashDf);
            if(llDFInst == null){
                llDFInst = new LinkedList();
                hashDFTypes.put(strHashDf, llDFInst);
            }
            
            llDFInst.add(df);
        }

        //Here we take each signature of the frequent dataflows and create the
        //itemsets that consists of parameter value assignments
        Iterator<String> itDFSig = hashDFTypes.keySet().iterator();
        while(itDFSig.hasNext()){
            String dfSig = itDFSig.next();

            //System.out.println(dfSig);

            LinkedList<Dataflow> llDFInst = hashDFTypes.get(dfSig);

            LinkedList<String> itemsets = new LinkedList();

            //Here we create the itemsets and use itemset mining to mine the
            //parameter values of the components involved in this Dataflow
            for(int i = 0; i < llDFInst.size(); i++){
                Dataflow df = llDFInst.get(i);

                Component fromComp = df.getFromComponent();
                Component toComp = df.getToComponent();

                //We get the parameter values for the source component
                String itemset = "";
                Iterator<InputParameter> itFromIp = fromComp.getInputParameters();
                while(itFromIp.hasNext()){
                    InputParameter ip = itFromIp.next();

                    if(ip.getValue() == null)
                            continue;

                    String paramName = ip.getName();

                    if(paramName.contains("#"))
                        System.exit(1);

                    String paramValue = ip.getValue();

                    paramName = paramName.trim();
                    paramValue = paramValue.trim();

                    if(paramValue.equals(""))
                        continue;


                    paramName = paramName.replaceAll("\\d", "");
                    paramName = paramName.replaceAll("_", "");

                    //We change everything to lower case to make user we have the same
                    //hash function value
                    paramValue = paramValue.toLowerCase();

                    itemset += fromComp.getType() + "." + paramName + "=>" + paramValue;

                    //We use a special carater to separate each item in the itemset
                    itemset += ITEM_SEPARATOR;

                }

                Iterator<InputParameter> itToIp = toComp.getInputParameters();
                while(itToIp.hasNext()){
                    InputParameter ip = itToIp.next();
                    
                    if(ip.getValue() == null)
                            continue;

                    String paramName = ip.getName();
                    String paramValue = ip.getValue();
                    
                    paramName = paramName.trim();
                    paramValue = paramValue.trim();

                    if(paramValue.equals(""))
                        continue;
                    

                    paramName = paramName.replaceAll("\\d", "");
                    paramName = paramName.replaceAll("_", "");
                    //System.out.println(paramName);

                    //We change everything to lower case to make user we have the same
                    //hash function value
                    paramValue = paramValue.toLowerCase();

                    itemset += toComp.getType() + "." + paramName + "=>" + paramValue;

                    //We use a special carater to separate each item in the itemset
                    itemset += ITEM_SEPARATOR;

                    //System.out.println("\t" + itemset);
                }

                if(!itemset.trim().equals("")){
                    itemsets.add(itemset);
                    //System.out.println("\t" + itemset);
                    
                }

            }

            int idx = 0;
            LinkedHashMap<String, Integer> hashStrToIdx = new LinkedHashMap();
            LinkedHashMap<Integer, String> hashIdxToStr = new LinkedHashMap();

            for(int i = 0; i < itemsets.size(); i++){
                String itemset = itemsets.get(i);

                //System.out.println(itemset);

                String[] items = itemset.split(ITEM_SEPARATOR);

                for(String item: items){
                    if(item.equals(""))
                        continue;
                    
                    if(hashStrToIdx.get(item) == null){
                        hashStrToIdx.put(item, idx);
                        hashIdxToStr.put(idx, item);
                        idx++;
                    }
                }
              
            }

            String[] strDB = new String[itemsets.size()];
            for(int i =0; i < itemsets.size(); i++){
                String strItemset = "";
                String itemset = itemsets.get(i);
                String[] items = itemset.split(ITEM_SEPARATOR);

                for(String item: items){
                    if(item.equals(""))
                        continue;

                    idx = hashStrToIdx.get(item);
                    strItemset += idx + " ";

                }
                strItemset = strItemset.trim();
                strDB[i] = strItemset;                
            }
            
            try{
                Carpenter carpenter = new Carpenter(strDB, minSupVa);
                List listPatterns = carpenter.mine();

               
                Iterator<int[]> itList = listPatterns.iterator();                
                while(itList.hasNext()){


                    //System.out.println(dfSig);

                    String[] pairWithPorts = dfSig.split("=>");

                    String auxLhs = pairWithPorts[0];
                    String auxRhs = pairWithPorts[1];

                    String[] fromPair = auxLhs.split("\\.");
                    String[] toPair = auxRhs.split("\\.");

                    String auxLhs2 = fromPair[0];
                    String auxRhs2 = fromPair[1];

                    String fromComp = auxLhs2;
                    String fromPort = auxRhs2;

                    String toComp = toPair[0];
                    String toPort = toPair[1];

                    CanonicalMashup cm = new CanonicalMashup();
                    cm.setId("");
                    cm.setName("");
                    cm.setSrc("");

                    Component fromCo = new Component();
                    fromCo.setId("");
                    fromCo.setName(fromComp);
                    fromCo.setType(fromComp);
                    fromCo.setIsEmbedding(false);

                    Component toCo = new Component();
                    toCo.setId("");
                    toCo.setName(toComp);
                    toCo.setType(toComp);
                    toCo.setIsEmbedding(false);

                    cm.addComponent(fromCo);
                    cm.addComponent(toCo);

                    Dataflow df = new Dataflow();
                    OutputPort outPort = new OutputPort();
                    outPort.setName(fromPort);
                    outPort.setType("");

                    InputPort inPort = new InputPort();
                    inPort.setName(toPort);
                    inPort.setType("");
                    
                    df.setFromPort(fromCo, outPort);
                    df.setToPort(toCo, inPort);

                    cm.addDataflow(df);
                    
                    int[] itemset = itList.next();
                    for(int item : itemset){
                        String strItem = (String)hashIdxToStr.get(item);

                        String[] pair = strItem.split("=>");
                        String lhs = pair[0];
                        
                        String paramValue = "";
                        if(pair.length == 2)
                            paramValue = pair[1];

                        //System.out.println(lhs);
                        String[] dotSep = lhs.split("\\.");
                        String strCompType = dotSep[0];
                        String strParamName = "";
                        if(dotSep.length == 3){
                            strParamName = dotSep[1] + "." + dotSep[2];
                        }else if(dotSep.length == 2){
                                strParamName = dotSep[1];
                        }else{
                            continue;
                        }




                        InputParameter ip = new InputParameter();
                        ip.setName(strParamName);
                        ip.setType("");
                        ip.setValue(paramValue);

                        if(fromCo.getType().equals(strCompType)){
                            fromCo.addInputParameter(ip);
                        }else if(toCo.getType().equals(strCompType)){
                            toCo.addInputParameter(ip);
                        }


                        //System.out.print("\t" + strItem + "     ");
                    }
                    result.add(cm);
                    //System.out.println(cm.toString());
                }


            }catch(Exception e){
                e.printStackTrace();
            }
             
        }


        /*
        //The following fragment of  code is just for computing statistics
        result = new LinkedList();
        Iterator it = hashReccDf.keySet().iterator();
        while(it.hasNext()){
            it.next();
            result.add(new CanonicalMashup());
        }
         */

        return result;
    }
    
}
